#include <linux/init.h>
#include <linux/module.h>
#include <linux/usb.h>
#include <linux/usb/input.h>
#include <linux/hid.h>
#include <linux/slab.h>
#include <linux/kernel.h>



#define DEAD_ZONE 10 //摇杆死区
#define X_ZERO 128
#define X_SCALE 60
#define Y_ZERO 118
#define Y_SCALE 70
#define Z_ZERO 130
#define Z_SCALE 70
#define Rx_ZERO 124
#define Rx_SCALE 70



//通用ID
static const struct hid_device_id my_joystick_id_table[] = {
     {  HID_USB_DEVICE(0xFFF0,0XFFF0) },
    {                                 }  //结束符
};

MODULE_DEVICE_TABLE (hid,my_joystick_id_table);
 

static int size;
static unsigned char *buf;
static struct urb *myurb;
static dma_addr_t buf_phy;
static struct input_dev *myusb_input_dev;//input_dev



static void myusb_irq(struct urb *urb)
{
	signed char X;
    signed char Y;
    signed char Z;
    signed char Rx;
    
    
    printk("X1:%d ",buf[0]);
    printk("Y1:%d ",buf[1]);
	printk("X2:%d ",buf[2]);
    printk("Y2:%d ",buf[3]);
    printk("BUTTON1:0x%x ",buf[4]);
    printk("BUTTON2:0x%x ",buf[5]);
    printk("BUTTON3:0x%x ",buf[6]);
    


    if((buf[0] < (X_ZERO + DEAD_ZONE)) && (buf[0] > (X_ZERO - DEAD_ZONE)))//在内死区
        X = 0;
    else if (buf[0] > (X_ZERO + X_SCALE))//在外死区
        X = 127;
    else if(buf[0] < (X_ZERO - X_SCALE))//在外死区
        X = -127;
    else
        X =  (buf[0] - X_ZERO) * 21166 / 10000;

    if((buf[1] < (Y_ZERO + DEAD_ZONE)) && (buf[1] > (Y_ZERO - DEAD_ZONE)))//在内死区
        Y = 0;
    else if (buf[1] > (Y_ZERO + Y_SCALE))//在外死区
        Y = 127;
     else if(buf[1] < (Y_ZERO - Y_SCALE))//在外死区
        Y = -127;
    else
        Y =  (buf[1] - Y_ZERO) * 18142 / 10000;

    
    if((buf[2] < (Z_ZERO + DEAD_ZONE)) && (buf[2] > (Z_ZERO - DEAD_ZONE)))//在内死区
        Z = 0;
    else if (buf[2] > (Z_ZERO + Z_SCALE))//在外死区
        Z = 127;
    else if(buf[2] < (Z_ZERO - Z_SCALE))//在外死区
        Z = -127;
    else
        Z =  (buf[2] - Z_ZERO) * 18142 / 10000;

    
    if((buf[3] < (Rx_ZERO + DEAD_ZONE)) && (buf[3] > (Rx_ZERO - DEAD_ZONE)))//在内死区
        Rx = 0;
    else if(buf[3] > (Rx_ZERO + Rx_SCALE))//在外死区
        Rx = 127;
    else if(buf[3] < (Rx_ZERO - Rx_SCALE))//在外死区
        Rx = -127;
    else
        Rx =  (buf[3] - Rx_ZERO) * 18142 / 10000;

    //Y = 0 - Y;//取相反
    //Rx = 0 - Rx;//取相反


    input_report_abs(myusb_input_dev, ABS_X, X);
    input_report_abs(myusb_input_dev, ABS_Y, Y);
    input_report_abs(myusb_input_dev, ABS_Z, Z);
    input_report_abs(myusb_input_dev, ABS_RZ, Rx);

    if((buf[6]&0x80)==0x80){
        input_report_key(myusb_input_dev, BTN_START, (buf[4]&0x01)==0x01);
        input_report_key(myusb_input_dev, BTN_THUMBR, (buf[4]&0x02)==0x02);
        input_report_key(myusb_input_dev, BTN_Y, (buf[4]&0x04)==0x04);
        input_report_key(myusb_input_dev, BTN_X, (buf[4]&0x08)==0x08);
        input_report_key(myusb_input_dev, KEY_VOLUMEDOWN, (buf[4]&0x10)==0x10);
        input_report_key(myusb_input_dev, KEY_VOLUMEUP, (buf[4]&0x20)==0x20);
        input_report_key(myusb_input_dev, BTN_TL2, (buf[4]&0x40)==0x40);
        input_report_key(myusb_input_dev, BTN_B, (buf[4]&0x80)==0x80);
        input_report_key(myusb_input_dev, BTN_A, (buf[5]&0x01)==0x01);
        input_report_key(myusb_input_dev, BTN_TL, (buf[5]&0x02)==0x02);
        input_report_key(myusb_input_dev, BTN_SELECT, (buf[5]&0x04)==0x04);
       
        //(for ABS values 0 is left/up, 1 is right/down)
        input_report_abs(myusb_input_dev, ABS_HAT0X,  !!(buf[5]&0x10) - !!(buf[5]&0x20) );
        input_report_abs(myusb_input_dev, ABS_HAT0Y,   !!(buf[5]&0x08) - !! (buf[5]&0x40) );

        input_report_key(myusb_input_dev, BTN_THUMBL, (buf[5]&0x80)==0x80);
        input_report_key(myusb_input_dev, BTN_TR, (buf[6]&0x01)==0x01);
        input_report_key(myusb_input_dev, BTN_TR2, (buf[6]&0x02)==0x02);
    }


    input_sync(myusb_input_dev);


	/* 重新提交异步请求*/
	usb_submit_urb(myurb, GFP_KERNEL);
}



static int my_joystick_probe(struct hid_device *hdev, const struct hid_device_id *id) 
{

    struct usb_interface *intf;
    struct usb_device *dev;
    struct usb_host_interface *interface;
	struct usb_endpoint_descriptor *endpoint;
    int pipe;  
    int retval;
    
    printk("my_joystick驱动开始匹配!");

    /*获取端点描述符*//*获取当前接口设置*/
    intf = to_usb_interface(hdev->dev.parent);
    dev = interface_to_usbdev(intf);
    interface = intf->cur_altsetting;
    endpoint = &interface->endpoint[0].desc;



    /*分配一个input_dev结构体*/
    myusb_input_dev = input_allocate_device();
    myusb_input_dev -> name = "GREENTOR_PAD";
    usb_to_input_id(dev, &myusb_input_dev->id);//通过USB设备生成input设备
    /*设置input_dev支持事件*/
    input_set_capability(myusb_input_dev, EV_ABS, ABS_X);
    input_set_capability(myusb_input_dev, EV_ABS, ABS_Y);
    input_set_capability(myusb_input_dev, EV_ABS, ABS_Z);//RX RY 还是Z RX还是Z RZ目前存疑
    input_set_capability(myusb_input_dev, EV_ABS, ABS_RZ);
    input_set_abs_params(myusb_input_dev, ABS_X, -127, 127, 0, 0);//设置绝对值区域
    input_set_abs_params(myusb_input_dev, ABS_Y, -127, 127, 0, 0);
    input_set_abs_params(myusb_input_dev, ABS_Z, -127, 127, 0, 0);
    input_set_abs_params(myusb_input_dev, ABS_RZ, -127, 127, 0, 0);
    input_set_capability(myusb_input_dev, EV_ABS, ABS_HAT0X);
    input_set_capability(myusb_input_dev, EV_ABS, ABS_HAT0Y);
    input_set_abs_params(myusb_input_dev, ABS_HAT0X, -1, 1, 0, 0);
    input_set_abs_params(myusb_input_dev, ABS_HAT0Y, -1, 1, 0, 0);
    input_set_capability(myusb_input_dev, EV_KEY, KEY_VOLUMEDOWN);
    input_set_capability(myusb_input_dev, EV_KEY, KEY_VOLUMEUP);
    input_set_capability(myusb_input_dev, EV_KEY, BTN_A);
    input_set_capability(myusb_input_dev, EV_KEY, BTN_B);
    input_set_capability(myusb_input_dev, EV_KEY, BTN_X);
    input_set_capability(myusb_input_dev, EV_KEY, BTN_Y);
    input_set_capability(myusb_input_dev, EV_KEY, BTN_TL);//左肩键
    input_set_capability(myusb_input_dev, EV_KEY, BTN_TR);//右肩键
    input_set_capability(myusb_input_dev, EV_KEY, BTN_TL2);//左板机，映射为按键
    input_set_capability(myusb_input_dev, EV_KEY, BTN_TR2);//右板机，映射为按键

    input_set_capability(myusb_input_dev, EV_KEY, BTN_START);//开始键
    input_set_capability(myusb_input_dev, EV_KEY, BTN_SELECT);//返回键
    input_set_capability(myusb_input_dev, EV_KEY, BTN_THUMBL);//左拇指
    input_set_capability(myusb_input_dev, EV_KEY, BTN_THUMBR);//右拇指

    /*注册input_dev结构体*/
    retval = input_register_device(myusb_input_dev);
    if(retval < 0) {
        dev_err(&hdev->dev, "Failed to register input device\n");
        input_free_device(myusb_input_dev);
        return retval;  // 返回错误代码
    }


    /*中断传输:创建输入管道*/
	pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);
	/*从端点描述符中获取传输的数据大小 */
	//size = endpoint->wMaxPacketSize;
    size = 7;//不知道为什么，读取设备的数据大小只会返回2，这里手动赋值
	printk("设备传输数据包大小:%d\n",size);
	/*分配数据传输缓冲区*/
	buf = usb_alloc_coherent(dev,size,GFP_ATOMIC,&buf_phy);

	/*分配新的urb,urb是usb设备驱动中用来描述与usb设备通信所用的基本载体和核心数据结构*/
	myurb = usb_alloc_urb(0,GFP_KERNEL);
	/*中断方式初始化urb*/
	usb_fill_int_urb(myurb,dev,pipe,buf,size,myusb_irq,NULL,endpoint->bInterval);
	myurb->transfer_dma = buf_phy;
	myurb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
	
	/*为端点提交异步传输请求*/
	usb_submit_urb(myurb, GFP_KERNEL);	

    printk("my_joystick驱动匹配成功!");

	return 0;
}
 
//USB断开的时候调用
static void my_joystick_disconnect(struct hid_device *hdev)
{
	struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
    struct usb_device *dev = interface_to_usbdev(intf);
    usb_kill_urb(myurb);
	usb_free_urb(myurb);
	usb_free_coherent(dev,size,buf, buf_phy);
    input_unregister_device(myusb_input_dev);//注销内核中的input_dev
    input_free_device(myusb_input_dev);//释放input_dev
    printk("my_joystick 设备释放成功!\n"); 
}
 
//定义USB驱动结构体 
static struct hid_driver my_joystick_driver = 
{
    .name = "my_joystick_drv",
    .probe = my_joystick_probe,
    .remove = my_joystick_disconnect,
    .id_table = my_joystick_id_table,
};


module_hid_driver(my_joystick_driver);


MODULE_AUTHOR(" GREENTOR");
MODULE_LICENSE("GPL");
